var HistoricalMap = Class.create({
    initialize : function(map, yearSlider, daySlider, eventControllerUrl) {
        this.map = map;
        this.yearSlider = yearSlider;
        this.daySlider = daySlider;
        this.eventControllerUrl = eventControllerUrl;
        this.manager = new MarkerManager(this.map, this.addMarkerListeners.bind(this));
        this.contextMenu = new ContextMenu(this.map);
        this.info = new google.maps.InfoWindow();
        this.timeoutId = null;
        google.maps.event.addListener(this.map, 'bounds_changed', this.getEvents.bind(this, 250));
        google.maps.event.addListener(this.map, 'click', this.onLeftClick.bind(this));
        google.maps.event.addListener(this.map, 'rightclick', this.onRightClick.bind(this));
        this.yearSlider.options.onChange = this.getEvents.bind(this);
        this.daySlider.options.onChange = this.getEvents.bind(this);
    },

    getEvents : function(interval) {
        // If time interval is specified, call getEvents after interval milliseconds
        if (interval)
        {
            clearTimeout(this.timeoutId);
            this.timeoutId = setTimeout(this.getEvents.bind(this), interval);
            return;
        }
        // Actual getting events using Ajax call
        var self = this;
        var region = self.map.getBounds();
        $('date').innerHTML = this.formatDate(Math.round(this.yearSlider.value), Math.round(this.daySlider.value));
        new Ajax.Request(self.eventControllerUrl,
        {
            method : 'get',
            parameters : {
                action : 'get',
                ne   : region.getNorthEast().toUrlValue(),
                sw   : region.getSouthWest().toUrlValue(),
                year : Math.round(this.yearSlider.value),
                day  : Math.round(this.daySlider.value)
            },
            onSuccess : function(transport) {
//                alert(transport.responseText);
                var events = transport.responseText.evalJSON();
                self.addEvents(events);
            },
            onFailure : function() {
                alert('An error occurred while trying to connect to '+self.eventControllerUrl+'.');
            }
        });
    },

    insertEvent : function(latLng) {
        var self = this;
        new Ajax.Request(self.eventControllerUrl,
        {
            method : 'get',
            parameters : {
                action : 'insert',
                latLng : latLng.toUrlValue(),
                year   : Math.round(this.yearSlider.value),
                day    : Math.round(this.daySlider.value)
            },
            onSuccess : function(transport) {
//                alert(transport.responseText);
                self.getEvents();
            },
            onFailure : function() {
                alert('An error occurred while trying to connect to '+self.eventControllerUrl+'.');
            }
        });
    },

    updateEventBegin : function(marker) {
        this.info.close();
        $('editEventForm:id').value = marker.id;
        $('editEventForm:title').value = marker.title;
        $('editEventForm:description').value = marker.description;
        $('editEventForm:type').value = marker.type;
        $('editEventForm:location').value = marker.getPosition().toUrlValue();
        $('editEventForm:date').value = marker.date;
    },

    updateEventEnd : function() {
        var self = this;
        new Ajax.Request(self.eventControllerUrl,
        {
            method : 'get',
            parameters : {
                action   : 'update',
                id       : $('editEventForm:id').value,
                title    : $('editEventForm:title').value,
                description : $('editEventForm:description').value,
                type     : $('editEventForm:type').value,
                latLng   : $('editEventForm:location').value,
                year     : self.parseDate($('editEventForm:date').value).year,
                day      : self.parseDate($('editEventForm:date').value).day
            },
            onSuccess : function() {
                self.getEvents();
            },
            onFailure : function() {
                alert('An error occurred while trying to connect to '+self.eventControllerUrl+'.');
            }
        });
    },

    deleteEvent : function(marker) {
        var self = this;
        new Ajax.Request(self.eventControllerUrl,
        {
            method : 'get',
            parameters : {
                action : 'delete',
                id : marker.id
            },
            onSuccess : function() {
                self.getEvents();
            },
            onFailure : function() {
                alert('An error occurred while trying to connect to '+self.eventControllerUrl+'.');
            }
        });
    },

    addEvents : function(events) {
        var self = this;
        var markers = events.map(function(event) {
            var marker = new google.maps.Marker({
                title : event.title || '-no title-',
                position : new google.maps.LatLng(event.lat, event.lng),
                icon : new google.maps.MarkerImage(event.iconUrl, new google.maps.Size(32, 32), new google.maps.Point(0, 0), new google.maps.Point(16, 16))
            });
            marker.id = event.id;
            marker.date = self.formatDate(parseInt(event.year), parseInt(event.day));
            marker.description = event.description || '-no description-';
            marker.type = event.type;
            marker.moreInfoLink = "http://pl.wikipedia.org/wiki/Bitwa_pod_Grunwaldem";
            return marker;
        });
        this.manager.setAll(markers);
    },

    onLeftClick : function() {
        this.info.close();
        this.contextMenu.hide();
    },
    
    onRightClick : function(e) {
        this.info.close();
        this.contextMenu.show(this.latLngToOffset(e.latLng), [
            {
                text : 'Insert New Event',
                handler : this.insertEvent.bind(this, e.latLng)
            }
        ]);
    },

    onMarkerLeftClick : function(marker) {
        this.contextMenu.hide();
        this.info.setContent('<p><strong>'+marker.title+' ('+marker.date+')</strong></p><p>'+marker.description+'</p><a href=\"'+marker.moreInfoLink+'\" target=\"blank\">more info</a>');
        this.info.open(this.map, marker);
    },

    onMarkerDoubleClick : function(marker) {
        this.contextMenu.hide();
        this.updateEventBegin(marker);
    },

    onMarkerRightClick : function(marker) {
        this.info.close();
        this.contextMenu.show(this.latLngToOffset(marker.getPosition()), [
            {
                text : 'Update Event',
                handler : this.updateEventBegin.bind(this, marker)
            },
            {
                text : 'Delete Event',
                handler : this.deleteEvent.bind(this, marker)
            }
        ]);
    },

    addMarkerListeners : function(marker) {
        google.maps.event.addListener(marker, 'click', this.onMarkerLeftClick.bind(this, marker));
        google.maps.event.addListener(marker, 'dblclick', this.onMarkerDoubleClick.bind(this, marker));
        google.maps.event.addListener(marker, 'rightclick', this.onMarkerRightClick.bind(this, marker));
    },

    latLngToOffset : function(latLng) {
        var region = this.map.getBounds();
        var ne_point = this.map.getProjection().fromLatLngToPoint(region.getNorthEast());
        var sw_point = this.map.getProjection().fromLatLngToPoint(region.getSouthWest());
        var point = this.map.getProjection().fromLatLngToPoint(latLng);
        var map_size = new google.maps.Size(parseInt(this.map.getDiv().style.width), parseInt(this.map.getDiv().style.height));
        var offset = new google.maps.Point(
            map_size.width*(point.x-sw_point.x)/(ne_point.x-sw_point.x),
            map_size.height*(point.y-ne_point.y)/(sw_point.y-ne_point.y)
        );
        return offset;
    },

    parseDate : function(date_string) {
        return {
            year : parseInt(date_string.substring(0, 4)),
            day : 0
        };
    },

    formatDate : function(year, day) {
        var date = new Date(day*24*60*60*1000);
        date.setFullYear(year, date.getMonth(), date.getDate());
        var y = date.getFullYear()+'';
        while (y.length < 4) y = '0'+y;
        var m = (date.getMonth()+1)+'';
        while (m.length < 2) m = '0'+m;
        var d = date.getDate()+'';
        while (d.length < 2) d = '0'+d;
        return y+'-'+m+'-'+d;
    }
});
